﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace WD.Shell
{
    public class ThemeResourceDictionary : ResourceDictionary
    {
    }

    public class ThemeSelector : DependencyObject
    {
        #region Current Theme Property and Apply method

        public static readonly DependencyProperty CurrentThemeDictionaryProperty =
            DependencyProperty.RegisterAttached("CurrentThemeDictionary", typeof(Uri),
            typeof(ThemeSelector),
            new UIPropertyMetadata(null, CurrentThemeDictionaryChanged));

        public static Uri GetCurrentThemeDictionary(DependencyObject obj)
        {
            return (Uri)obj.GetValue(CurrentThemeDictionaryProperty);
        }

        public static void SetCurrentThemeDictionary(DependencyObject obj, Uri value)
        {
            obj.SetValue(CurrentThemeDictionaryProperty, value);
        }

        private static void CurrentThemeDictionaryChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is FrameworkElement) // works only on FrameworkElement objects
            {
                ApplyTheme(obj as FrameworkElement, GetCurrentThemeDictionary(obj));
            }
        }

        private static void ApplyTheme(FrameworkElement targetElement, Uri dictionaryUri)
        {
            if (targetElement == null) return;

            try
            {
                ThemeResourceDictionary themeDictionary = null;
                if (dictionaryUri != null)
                {
                    themeDictionary = new ThemeResourceDictionary {Source = dictionaryUri};

                    // add the new dictionary to the collection of merged dictionaries of the target object
                    targetElement.Resources.MergedDictionaries.Insert(0, themeDictionary);
                }

                // find if the target element already has a theme applied
                List<ThemeResourceDictionary> existingDictionaries =
                    (from dictionary in targetElement.Resources.MergedDictionaries.OfType<ThemeResourceDictionary>()
                     select dictionary).ToList();

                // remove the existing dictionaries 
                foreach (ThemeResourceDictionary thDictionary in existingDictionaries)
                {
                    if (themeDictionary == thDictionary) continue;  // don't remove the newly added dictionary
                    targetElement.Resources.MergedDictionaries.Remove(thDictionary);
                }
            }
            catch (Exception e)
            {
            }
        }

        #endregion

        #region Global Theme

        private static readonly List<FrameworkElement> ElementsWithGlobalTheme = new List<FrameworkElement>();

        private static Uri _globalThemeDictionary;

        public static Uri GlobalThemeDictionary
        {
            get { return _globalThemeDictionary; }
            set
            {
                _globalThemeDictionary = value;

                // apply to all elements registered to use the global theme
                foreach (FrameworkElement element in ElementsWithGlobalTheme)
                {
                    if (GetApplyGlobalTheme(element))
                        ApplyTheme(element, _globalThemeDictionary);
                }
            }
        }

        public static readonly DependencyProperty ApplyGlobalThemeProperty =
            DependencyProperty.RegisterAttached("ApplyGlobalTheme", typeof(bool),
            typeof(ThemeSelector),
            new UIPropertyMetadata(false, ApplyGlobalThemeChanged));

        public static bool GetApplyGlobalTheme(DependencyObject obj)
        {
            return (bool)obj.GetValue(ApplyGlobalThemeProperty);
        }

        public static void SetApplyGlobalTheme(DependencyObject obj, bool value)
        {
            obj.SetValue(ApplyGlobalThemeProperty, value);
        }


        private static void ApplyGlobalThemeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is FrameworkElement)
            {
                var element = obj as FrameworkElement;
                if ((bool)e.NewValue)   // if property is changed to 'true', then add to the list of elements and apply theme
                {
                    if (!ElementsWithGlobalTheme.Contains(element))
                        ElementsWithGlobalTheme.Add(element);

                    // apply the theme
                    ApplyTheme(element, GlobalThemeDictionary);
                }
                else
                {
                    if (ElementsWithGlobalTheme.Contains(element))
                        ElementsWithGlobalTheme.Remove(element);

                    // apply the local theme instead of the global
                    ApplyTheme(element, GetCurrentThemeDictionary(element));
                }
            }
        }


        #endregion

    }
}
